#!/bin/sh

. $LKP_SRC/lib/env.sh
. $LKP_SRC/lib/curl.sh

setup_curl

set_http_host()
{
    if [ -n "$result_host" ]; then # new scheduler
        http_host="http://${result_host}:${result_port}"
    else
        http_host="http://${RESULT_WEBDAV_HOST:-$LKP_SERVER}:${RESULT_WEBDAV_PORT:-3080}"
    fi
}

has_rsync_server()
{
	[ -n "$RSYNC_SERVER" ] && return
	[ -e $LKP_SRC/etc/trustable-lkp-server ] || return

	echo "$LKP_SERVER" | grep -q -E -f $LKP_SRC/etc/trustable-lkp-server
}

upload_files_rsync()
{
	[ -n "$RSYNC_SERVER" ] || local RSYNC_SERVER=$LKP_SERVER

	[ -n "$target_directory" ] && {

		local current_dir=$(pwd)
		local tmpdir=$(mktemp -d)
		cd "$tmpdir"
		mkdir -p ${target_directory}

		rsync -a --no-owner --no-group \
			--chmod=Dug=rwx,Do=rx,Fug=rw,Fo=r \
			--min-size=1 \
			${target_directory%%/*} rsync://$RSYNC_SERVER$JOB_RESULT_ROOT/

		local JOB_RESULT_ROOT=$JOB_RESULT_ROOT/$target_directory

		cd $current_dir
		rm -fr "$tmpdir"
	}

	rsync -a --no-owner --no-group \
		--chmod=Dug=rwx,Do=rx,Fug=rw,Fo=r \
		--min-size=1 \
		"$@" rsync://$RSYNC_SERVER$JOB_RESULT_ROOT/
}

upload_files_lftp()
{
	local file
	local dest_file
	local ret=0
	local LFTP_TIMEOUT='set net:timeout 2; set net:reconnect-interval-base 2; set net:max-retries 2;'

    local http_host
    set_http_host

	[ -n "$target_directory" ] && {
		local JOB_RESULT_ROOT=$JOB_RESULT_ROOT/$target_directory
		lftp -c "$LFTP_TIMEOUT; open '$http_host'; mkdir -p '$JOB_RESULT_ROOT'"
	}

	for file
	do
		if [ -d "$file" ]; then
			[ "$(ls -A $file)" ] && lftp -c "$LFTP_TIMEOUT; open '$http_host'; mirror -R '$file' '$JOB_RESULT_ROOT/'" || ret=$?
		else
			[ -s "$file" ] || continue
			dest_file=$JOB_RESULT_ROOT/$(basename $file)

			lftp -c "$LFTP_TIMEOUT; open '$http_host'; put -c '$file' -o '$dest_file'" || ret=$?
		fi
	done

	return $ret
}

upload_one_curl()
{
    local src="$1"
    local dest="$2"

    if [ -n "$result_host" ]; then # new scheduler
        upload_one_srv "$src" "${dest}"
        return
    fi

	local http_host
    set_http_host

    local http_url="$http_host$dest"

    # Check if the source is a directory
    if [ -d "$src" ]; then
        (
            cd "$(dirname "$src")" || exit
            dir="$(basename "$src")"

            # Handle directories
            if [ -n "$id" ]; then
                find "$dir" -type d -exec $http_client_cmd -X MKCOL "$http_url/{}/" --cookie "JOBID=$id" \;
                find "$dir" -type f -size +0 -exec $http_client_cmd -T '{}' "$http_url/{}" --cookie "JOBID=$id" \;
            else
                find "$dir" -type d -exec $http_client_cmd -X MKCOL "$http_url/{}/" \;
                find "$dir" -type f -size +0 -exec $http_client_cmd -T '{}' "$http_url/{}" \;
            fi
        )
    else
        # Skip if the source file is empty or does not exist
        [ -s "$src" ] || return

        if [ -n "$id" ]; then
            i=1
            while [ "$i" -le 20 ]; do
                if [ "$i" -gt 1 ]; then
                    sleep 6
                fi
                res=$($http_client_cmd -F "file=@$src" "$http_url/" --cookie "JOBID=$id")
                echo "$res" | grep -q '"status":0' && return 0
                i=$((i + 1))
            done
            return 1
        else
            $http_client_cmd -T "$src" "$http_url/"
        fi
    fi
}

upload_files_curl()
{
    if [ -n "$result_host" ]; then # new scheduler
        upload_many_result "${target_directory:+-t $target_directory}" "$@"
        return
    fi

	local file
	local ret=0

	local http_host
    set_http_host

	# "%" character as special character not be allowed in the URL when use curl command to transfer files, details can refer to below:
	# https://www.werockyourweb.com/url-escape-characters/
	local job_result_root=$(echo $JOB_RESULT_ROOT | sed 's/%/%25/g')

	[ -n "$target_directory" ] && {
		local dir
		for dir in $(echo $target_directory | tr '/' ' ')
		do
			job_result_root=$job_result_root/$dir/
			if [ -n "$id" ]; then
				$http_client_cmd -X MKCOL "$http_host$job_result_root" --cookie "JOBID=$id"  >/dev/null
			else
				$http_client_cmd -X MKCOL "$http_host$job_result_root"  >/dev/null
			fi
		done
	}

	for file
	do
		upload_one_curl "$file" "$job_result_root" >/dev/null || ret=$?
	done

	return $ret
}

upload_files_copy()
{
	local RESULT_ROOT="$RESULT_ROOT/$target_directory"

	mkdir -p $RESULT_ROOT

	if [ "$LKP_LOCAL_RUN" != "1" ]; then
		[ -n "$target_directory" ] && {
			chown -R lkp.lkp $RESULT_ROOT
			chmod -R g+w $RESULT_ROOT
		}

		chown -R lkp.lkp "$@"
		chmod -R ug+w "$@"
	fi

	local copy="cp -a"
	local file
	local ret=0

	for file
	do
		[ -s "$file" ] || continue
		[ "$LKP_LOCAL_RUN" = "1" ] && chmod ug+w "$file"
		$copy "$file" $RESULT_ROOT/ || {
			ls -l "$@" $RESULT_ROOT 2>&1
			ret=$?
		}
	done

	return $ret
}

upload_files()
{
	if [ "$1" = "-t" ]; then
		local target_directory="$2"
		shift 2
	fi

	[ $# -ne 0 ] || return

	# NO_NETWORK is empty: means network is avaliable
	if [ -z "$NO_NETWORK$UPLOAD_BY_COPY_TO" ]; then
		[ -z "$JOB_RESULT_ROOT" -a "$LKP_LOCAL_RUN" = "1" ] && { # bin/run-local.sh
			upload_files_copy "$@"
			return
		}
		if has_cmd rsync && has_rsync_server && [ -z "$id" ]; then
			upload_files_rsync "$@"
			return
		fi

		if has_cmd lftp && [ -z "$id" ]; then
			upload_files_lftp "$@"
			return
		fi

		if has_cmd curl; then
			upload_files_curl "$@"
			return
		fi
	else
		# 9pfs, copy directly
		upload_files_copy "$@"
		return
	fi
}

# Function to upload a single file
upload_one_result() {
    local local_file="$1"
    local remote_path="${2:-$(basename "$local_file")}"
    upload_one_srv "$1" "${JOB_RESULT_ROOT}/$remote_path"
}

upload_one_srv() {
  local local_file="$1"
  local target_path="$2"
  local retries=5
  local delay=5

  # URL encode the target_path (important for spaces, etc.)
  local encoded_target_path=$(echo "$target_path" | sed -e 's/%/%25/g' -e 's/ /%20/g' -e 's/!/%21/g' -e 's/"/%22/g' -e 's/#/%23/g' -e 's/\$/%24/g' -e 's/&/%26/g' -e 's/'\''/%27/g' -e 's/(/%28/g' -e 's/)/%29/g' -e 's/\*/%2A/g' -e 's/+/%2B/g' -e 's/,/%2C/g' -e 's/:/%3A/g' -e 's/;/%3B/g' -e 's/=/%3D/g' -e 's/?/%3F/g' -e 's/@/%40/g' -e 's/\[/%5B/g' -e 's/\\/%5C/g' -e 's/\]/%5D/g' -e 's/\^/%5E/g' -e 's/`/%60/g' -e 's/{/%7B/g' -e 's/|/%7C/g' -e 's/}/%7D/g' -e 's/~/%7E/g')

  local http_host
  set_http_host

  local http_url="$http_host$encoded_target_path?job_id=$id&job_token=$job_token"

  local max_retries=50
  local retry_delay=5
  local retry_count=0

  while : ; do
    response=$($http_client_cmd -X POST -w "%{http_code}" --data-binary "@$local_file" "$http_url")
    # echo "$response"

    case "$response" in
      *200|*201)
        echo "Upload successful: $local_file to $target_path"
        return 0
        ;;
      *409)
        echo "Error: File already exists on server: $target_path"
        return 1
        ;;
      *403)
        echo "Error: Authentication failed or job stage invalid"
        return 1
        ;;
      *400)
        echo "Error: Bad request: check parameters"
        return 1
        ;;
      *)
        if [ $retry_count -lt $max_retries ]; then
          echo "Upload failed (HTTP $response). Retrying in $retry_delay seconds..."
          sleep $retry_delay
          retry_count=$((retry_count+1))
          retry_delay=$((retry_delay*2))
        else
          echo "Upload failed after $max_retries attempts"
          return 1
        fi
        ;;
    esac
  done
}

# Function to upload multiple files or directories
upload_many_result() {
  local target_dir=""
  local files=""

  # Parse arguments
  while [ $# -gt 0 ]; do
    case "$1" in
      -t|--target-directory)
        target_dir="$2"
        shift 2
        ;;
      *)
        files="$files${files:+ }$1"
        shift
        ;;
    esac
  done

  for file in $files; do
    if [ ! -e "$file" ]; then
      echo "Error: Path does not exist: $file" >&2
      return 1
    fi

    if [ -f "$file" ]; then
      filename=$(basename "$file")
      remote_path="${target_dir:+$target_dir/}$filename"
      upload_one_result "$file" "$remote_path" || return 1
    elif [ -d "$file" ]; then
      base_dir=$(basename "$file")
      find "$file" -type f | while IFS= read -r file; do
        rel_path="${file#$file/}"
        remote_path="${target_dir:+$target_dir/}$base_dir/$rel_path"
        upload_one_result "$file" "$remote_path" || return 1
      done
    else
      echo "Error: $file is not a valid file or directory" >&2
      return 1
    fi
  done
}

# vim:set ts=4 sw=4 et:
